package com.rrjjg.app.core.sign;


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.rrjjg.app.core.exception.RjException;
import com.rrjjg.app.core.properties.SignJwtProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

import static com.rrjjg.app.core.Constants.JWT_BODY;
import static com.rrjjg.app.core.Constants.JWT_HEADER;
import static com.rrjjg.app.core.exception.MsgEnum.JWT_CHECK_ERR;
import static com.rrjjg.app.core.exception.MsgEnum.JWT_CREATE_ERR;

@Slf4j
public class DefaultJwtHelper<E extends Map<String,Object>> extends AbstractJwtHelper<Map<String,Object>>{

    private final ThreadLocal<Map<String,Object>> local ;

    public DefaultJwtHelper(SignJwtProperties signJwtProperties) {
        super(signJwtProperties);
        this.local = new ThreadLocal<>();
    }

    @Override
    public String doCreate(Map<String, Object> map) {
        try{
            if (CollectionUtils.isEmpty(map)) {
                throw new RjException(JWT_CREATE_ERR);
            }
            Calendar expires = Calendar.getInstance();
            expires.add(Calendar.SECOND, signJwtProperties.getSecond());
            //第一部分
            String sign = JWT.create()
                    .withHeader((Map<String, Object>) map.get(JWT_HEADER))
                    .withClaim(JWT_BODY, (Map<String, Object>) map.get(JWT_BODY))
                    .withExpiresAt(expires.getTime())
                    .sign(Algorithm.HMAC256(signJwtProperties.getSalt()));
            return sign;
        }catch (Exception ex){
            log.error("token create error",ex);
            if(ex instanceof RjException){
                throw ex;
            }
            throw new RjException(JWT_CREATE_ERR);
        }

    }

    @Override
    public Map<String, Object> doCheck(String token) {
        try{
            if (StringUtils.isEmpty(token)) {
                throw new RjException(JWT_CHECK_ERR);
            }
            JWTVerifier build = JWT.require(Algorithm.HMAC256(signJwtProperties.getSalt())).build();
            DecodedJWT verify = build.verify(token);
            Map<String, Claim> claims = verify.getClaims();
            Date expiresAt = verify.getExpiresAt();
            long time = expiresAt.getTime();
            if (time < System.currentTimeMillis()) {
                throw new RjException(JWT_CHECK_ERR);
            }
            return claims.get(JWT_BODY).asMap();
        }catch (Exception ex){
            log.error("token check error",ex);
            if(ex instanceof RjException){
                throw ex;
            }
            throw new RjException(JWT_CHECK_ERR);
        }

    }


    @Override
    public String[] exclude() {
        try{
            return signJwtProperties.getExclude();
        }catch (Exception ex){
            log.error("token exclude error",ex);
            if(ex instanceof RjException){
                throw ex;
            }
            throw new RjException(JWT_CHECK_ERR);
        }

    }

    @Override
    public void clearOnce(String token) {
        try{
            local.remove();
        }catch (Exception ex){
            log.error("token clear error",ex);
            if(ex instanceof RjException){
                throw ex;
            }
            throw new RjException(JWT_CHECK_ERR);
        }
    }

    @Override
    public void saveOnce(Map<String, Object> map) {
        try{
            local.set(map);
        }catch (Exception ex){
            log.error("token save error",ex);
            if(ex instanceof RjException){
                throw ex;
            }
            throw new RjException(JWT_CHECK_ERR);
        }
    }

    @Override
    public Map<String, Object> get(String token) {
        try{
            return local.get();
        }catch (Exception ex){
            log.error("token get error",ex);
            if(ex instanceof RjException){
                throw ex;
            }
            throw new RjException(JWT_CHECK_ERR);
        }

    }

}
